import { useMemo, useEffect } from 'react';
import { useSelector, useDispatch } from 'dva';
import { Common } from 'UtilsCommon';

export default (app, mapStateToProps) => {
  const [props, state] = useSelector((state) => [mapStateToProps(state), state]);

  const dispatch = useDispatch();

  const obj = useMemo(() => ({}), []);

  init(obj, dispatch, app, state);

  useEffect(() => () => { obj.isDestory = true; }, [obj]);

  const getActionState = (serviceName, stateName) => {
    if (state[serviceName]) return state[serviceName][stateName];
    return null;
  };

  return [obj.dispatch, obj.dispatchAction, obj.setActionState, getActionState, props];
};

function init(obj, dispatch, app, state) {
  if (!obj.isInit) obj.isInit = true; else return;

  obj.dispatch = getDispatch(dispatch, app);
  obj.dispatchAction = dispatchAction(obj, dispatch, app);
  obj.setActionState = setActionState(dispatch);
}

function getDispatch(dispatch, app) {
  return (name, actionName, payload) => {
    let isloading = true;
    payload = payload || {};

    const action = getModelAction(name, actionName, app);
    if (action !== null) {
      if (action.isOperation) setActionState(dispatch)(name, actionName);
    }
    else return Promise.reject({ isSuccess: false, message: `${name}/${actionName} the method doesn't exist!` });

    if (action.isToken && !payload.token) payload.token = window.token;
    if (action.isLoading === false || payload.isLoading === false) isloading = false;
    return dispatch({ type: `${name}/${actionName}`, payload, isloading });
  };
}

function dispatchAction(obj, dispatch, app) {
  return (name, actionName, payload) => getDispatch(dispatch, app)(name, actionName, payload).then(res => {
    if (obj.isDestory && (!payload || !payload.isCanDestory)) res = { isSuccess: false };
    return Promise.resolve(res);
  }, res => {
    if (obj.isDestory && (!payload || !payload.isCanDestory)) res = { isSuccess: false };
    return Promise.resolve(res);
  });
}

function setActionState(dispatch) {
  return (name, actionName, payload) => dispatch({ type: `${name}/set_${actionName}`, payload });
}

function getModelAction(name, actionName, app) {
  const { _models } = app;
  if (!_models) return null;
  const model = Common.arrayFirst(_models, f => f.namespace === name);
  if (model && model.actions) return Common.arrayFirst(model.actions, f => f.actionName === actionName);
  return null;
}
